package org.springboot.jdbctemplate.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

@Configuration
public class TransactionConfig {

	/**
	 * 事务四大特性(ACID)：这四个特性中，只有隔离性（隔离级别）是可以设置的
	 *  - 原子性(Atomicity)：一个事务中的所有操作，要么全部完成，要么全部不完成。不会结束在中间某个环节。事务在执行过程中发生错误，会被回滚到事务开始前的状态
	 *  - 一致性(Consistency)：在事务开始之前和事务事务结束之后，数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则，这包含资料的精准度、串联性以及后续数据库可以自发性地完成预定的工作
	 *  - 持久性(Isolation)：事务处理结束后，对数据的修改就是永久的。即使系统故障也不会丢失
	 *  - 隔离性(Durability)：数据库允许多个并发事务同时对其数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由交叉执行而导致数据的不一致
	 * 编程式事务：通过编写具体代码来实现事务管理的方式. 手动控制事务的开始、提交和回滚等操作
	 *   Spring Boot 内置了两个对象：
	 *   - DataSourceTransactionManager（事务管理器）用于开启、提交或回滚事务
	 *   - TransactionDefinition（事务的属性）获取事务的时候需要将 TransactionDefinition 传递进去从而获得一个事务 TransactionStatus 对象
	 *   - TransactionTemplate 是对 TransactionDefinition 和 DataSourceTransactionManager 的封装
	 * 声明式事务：配置 @Transactional 注解来管理事务，而不需要依赖底层 API 进行硬编码
	 *   - 只能应用于公共方法(public)
	 *   - propagation 配置错误会导致事务不生效，也可能导致事务嵌套问题
	 *   - rollbackFor 设置错误会导致事务不生效
	 *   - 同一个类中方法调用，导致 @Transactional 失效，比如有一个类 Test，它的一个方法 A，A 再调用本类的方法 B（不论方法 B 是用 public 还是 private 修饰），但方法 A 没有声明注解事务，而 B 方法有。则外部调用方法 A 之后，方法 B 的事务是不会起作用的
	 *     由于使用 Spring AOP 代理造成的，因为只有当事务方法被当前类以外的代码调用时，才会由 Spring 生成的代理对象来管理，使用该类的 Spring 代理对象调用方法（推荐）或其它可以获取 spring 对象的方式
	 *   - 异常被 catch 处理导致 @Transactional 失效
	 *   - 数据库引擎不支持事务，概率很小
	 *   - 没有启用 AOP 代理
	 *   - 如果设置了事务的超时时间，但在超时时间内事务未能完成，则事务会回滚
	 * 注解属性介绍
	 *  propagation 事务传播行为
	 *   - Propagation.REQUIRED：默认，如果当前存在事务，则加入该事务，如果当前不存在事务，则创建一个新的事务(也就是说如果 A 方法和 B 方法都添加了注解，在默认传播模式下，A 方法内部调用 B 方法，会把两个方法的事务合并为一个事务）
	 *   - Propagation.SUPPORTS：如果当前存在事务，则加入该事务；如果当前不存在事务，则以非事务的方式继续运行
	 *   - Propagation.MANDATORY：如果当前存在事务，则加入该事务；如果当前不存在事务，则抛出异常
	 *   - Propagation.REQUIRES_NEW：重新创建一个新的事务，如果当前存在事务，暂停当前的事务(当类 A 中的 a 方法用默认 Propagation.REQUIRED 模式，类 B 中的 b 方法加上采用 Propagation.REQUIRES_NEW模式，然后在 a 方法中调用 b 方法操作数据库，然而 a 方法抛出异常后，b 方法并没有进行回滚，因为 Propagation.REQUIRES_NEW 会暂停 a 方法的事务)
	 *   - Propagation.NOT_SUPPORTED：以非事务的方式运行，如果当前存在事务，暂停当前的事务
	 *   - Propagation.NEVER：以非事务的方式运行，如果当前存在事务，则抛出异常
	 *   - Propagation.NESTED：和 Propagation.REQUIRED 效果一样
	 *  isolation 事务隔离级别
	 *   - Isolation.DEFAULT：使用底层数据库默认的隔离级别
	 *   - Isolation.READ_UNCOMMITTED：读未提交，脏读，所以存在不可重复读和幻读
	 *   - Isolation.READ_COMMITTED：读已提交，不可重复读
	 *   - Isolation.REPEATABLE_READ：可重复读，幻读
	 *   - Isolation.SERIALIZABLE：串行化，事务最高的隔离级别，效率低
	 *  timeout 事务的超时时间，默认值为 -1。如果超过该时间限制但事务还没有完成，则自动回滚事务
	 *  readOnly 指定事务是否为只读事务，默认值为 false；为了忽略那些不需要事务的方法，比如读取数据，可以设置 read-only 为 true
	 *  rollbackFor 用于指定能够触发事务回滚的异常类型，可以指定多个异常类型
	 *  noRollbackFor 抛出指定的异常类型，不回滚事务，也可以指定多个异常类型
	 */
	
	@Autowired
    @Qualifier("oneJdbcTemplate")
    private JdbcTemplate jdbcTemplate;
	
	// 事务管理器
	private PlatformTransactionManager transactionManager;

	public synchronized PlatformTransactionManager getTxManager() {
		if (transactionManager == null) {
			return new DataSourceTransactionManager(jdbcTemplate.getDataSource());
		}
	    return transactionManager;
	}
	
	// 事务属性
	private TransactionTemplate transactionTemplate;
	
	public synchronized TransactionTemplate getTxTemplate() {
		if (transactionTemplate == null) {
			transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(jdbcTemplate.getDataSource()));
		}
		return transactionTemplate;
	}

}
